\subsection{Безразмерный массив в структуре Си}

В некоторый win32-структурах мы можем найти такие, где последнее поле определено как массив из одного элемента:

\begin{lstlisting}[style=customc]
typedef struct _SYMBOL_INFO {
  ULONG   SizeOfStruct;
  ULONG   TypeIndex;
  
  ...

  ULONG   MaxNameLen;
  TCHAR   Name[1];
} SYMBOL_INFO, *PSYMBOL_INFO;
\end{lstlisting}

( \url{https://msdn.microsoft.com/en-us/library/windows/desktop/ms680686(v=vs.85).aspx} )

Это хак, в том смысле, что последнее поле это массив неизвестной длины, его размер будет вычислен во время выделения
памяти под структуру.

Вот почему: поле \IT{Name} может быть коротким, так зачем же тогда определять константу вроде \IT{MAX\_NAME},
которая может быть 128, 256, или даже больше?

Почему вместо этого не использовать указатель? Но тогда придется выделять два блока: один под структуру и второй
под строку.
Это может быть медленнее и может требовать больше затрат на память.
Также, вам нужно разыменовывать указатель (т.е., читать адрес строки из структуры) --- не очень большая проблема,
но некоторые люди могут сказать вам, что это дополнительные расходы.

Это также известно как \IT{struct hack}: \url{http://c-faq.com/struct/structhack.html}.

Например:

\begin{lstlisting}[style=customc]
#include <stdio.h>

struct st
{
	int a;
	int b;
	char s[];
};

void f (struct st *s)
{
	printf ("%d %d %s\n", s->a, s->b, s->s);
	// §f() не может заменить s[] большей строкой - длина выделенного блока неизвестна на этом этапе§
};

int main()
{
#define STRING "Hello!"
	struct st *s=malloc(sizeof(struct st)+strlen(STRING)+1); // §включая терминирующий ноль.§
	s->a=1;
	s->b=2;
	strcpy (s->s, STRING);
	f(s);
};
\end{lstlisting}

Если коротко, это работает, потому что в Си нет проверок границ массивов. К любому массиву относятся так, будто он бесконечный.

Проблема: после выделения, полный размер выделенного блока для структуры неизвестен (хотя известен менеджеру памяти),
так что вы не можете заменить строку б\'{о}льшей строкой.
Но вы бы смогли делать это, если бы поле было определено как что-то вроде \IT{s[MAX\_NAME]}.

Другими словами, вы имеете структуру плюс массив (или строку) спаянных вместе в одном выделенном блоке памяти.
Другая проблема еще в том, что вы не можете объявить два таких массива в одной структуре, или объявить еще одно поле
после такого массива.

Более старые компиляторы требуют объявить массив хотя бы с одним элементом: \IT{s[1]}, более новые позволяют
определять его как массив с переменной длиной: \IT{s[]}.
В стандарте C99 это также называется \IT{flexible array member}\footnote{\url{https://en.wikipedia.org/wiki/Flexible_array_member}}.

Читайте об этом больше в документации GCC\footnote{\url{https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html}},
в документации MSDN\footnote{\url{https://msdn.microsoft.com/en-us/library/b6fae073.aspx}}.

Деннис Ритчи (один из создателей Си) называет этот трюк \q{unwarranted chumminess with the C implementation} 
(вероятно, подтверждая хакерскую природу трюка).

Вам это может нравится, или нет, вы можете использовать это или нет:
но это еще одна демонстрация того, как структуры располагаются в памяти, вот почему я написал об этом.

